import can
import time
import datetime
import threading


class CANTransmitter:
    def __init__(self, interface='socketcan', channel='can0',
                 tx_id=0x123, interval_ms=20):
        """
        初始化CAN发送器

        参数:
            interface: CAN接口类型 (socketcan, pcan, virtual等)
            channel: 接口通道 (can0, PCAN_USBBUS1, virtual_channel等)
            tx_id: 发送消息的CAN ID
            interval_ms: 发送间隔(毫秒)
        """
        # 初始化CAN总线
        self.bus = can.interface.Bus(
            interface=interface,
            channel=channel,
            bitrate=500000
        )

        self.tx_id = tx_id
        self.interval_ms = interval_ms
        self.running = False
        self.stop_event = threading.Event()
        self.send_thread = threading.Thread(target=self.cyclic_send_task)

        print(f"CAN发送器初始化完成")
        print(f"接口: {interface}, 通道: {channel}")
        print(f"发送ID: 0x{tx_id:X}, 周期: {interval_ms}ms")

    def start(self):
        """启动发送器"""
        if self.running:
            print("发送器已在运行中")
            return

        self.running = True
        self.stop_event.clear()
        self.send_thread.start()
        print("CAN发送器已启动")

    def stop(self):
        """停止发送器"""
        if not self.running:
            print("发送器未运行")
            return

        self.running = False
        self.stop_event.set()
        if self.send_thread.is_alive():
            self.send_thread.join(timeout=1.0)
        print("CAN发送器已停止")

    def send_message(self, data):
        """发送自定义数据消息"""
        try:
            msg = can.Message(
                arbitration_id=self.tx_id,
                data=data,
                is_extended_id=False
            )
            self.bus.send(msg)
            timestamp = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3]
            print(f"[{timestamp}] TX: ID=0x{self.tx_id:03X}, DLC={len(data)}, Data={data.hex(' ').upper()}")
            return True
        except can.CanError as e:
            print(f"发送失败: {e}")
            return False

    def send_diagnostic_request(self, service_id, data=None):
        """发送诊断请求"""
        if data is None:
            data = []
        request_data = bytes([service_id] + data)
        return self.send_message(request_data)

    def cyclic_send_task(self):
        """定时发送CAN消息的任务"""
        counter = 0
        print(f"开始周期发送 (间隔: {self.interval_ms}ms)")

        while not self.stop_event.is_set():
            start_time = time.perf_counter()

            # 创建要发送的消息数据
            data = [
                counter & 0xFF,  # 计数器低字节
                (counter >> 8) & 0xFF,  # 计数器高字节
                0xAA, 0xBB, 0xCC, 0xDD  # 示例数据
            ]

            # 发送消息
            self.send_message(bytes(data))

            # 更新计数器
            counter = (counter + 1) % 65536

            # 精确时间控制
            elapsed = (time.perf_counter() - start_time) * 1000  # 转换为毫秒
            sleep_time = max(0, self.interval_ms - elapsed) / 1000.0

            if sleep_time > 0:
                time.sleep(sleep_time)

        print("周期发送任务已停止")

    def shutdown(self):
        """关闭资源"""
        self.stop()
        self.bus.shutdown()
        print("总线资源已释放")


if __name__ == "__main__":
    # 配置选项
    config = {
        'interface': 'virtual',  # 使用虚拟总线
        'channel': 'virtual_channel',  # 虚拟通道
        'tx_id': 0x7E0,  # 发送ID
        'interval_ms': 20  # 发送间隔(毫秒)
    }

    # 创建发送器实例
    transmitter = CANTransmitter(**config)

    try:
        # 启动发送器
        transmitter.start()

        # 等待用户按下Ctrl+C结束程序
        print("程序正在运行，按Ctrl+C停止...")

        # 示例：添加一些诊断请求
        time.sleep(2)
        print("\n发送诊断会话控制请求...")
        transmitter.send_diagnostic_request(0x10, [0x01])  # 切换到扩展会话

        time.sleep(2)
        print("\n发送读取数据请求...")
        transmitter.send_diagnostic_request(0x22, [0xF1, 0x90])  # 读取标识符0xF190

        time.sleep(2)
        print("\n发送测试器在线请求...")
        transmitter.send_diagnostic_request(0x3E)  # 测试器在线

        # 主循环
        while True:
            time.sleep(1)

    except KeyboardInterrupt:
        print("\n用户中断程序，正在关闭资源...")
    finally:
        transmitter.shutdown()